/*  armemu.c -- Main instruction emulation:  ARM6 Instruction Emulator.
    Copyright (C) 1994 Advanced RISC Machines Ltd.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

    /* This copy incorporates a number of bug fixes by David Alan Gilbert
    (arcem@treblig.org) */

#include "armdefs.h"
#include "archio.h"
#include <time.h>

/***************************************************************************\
*                               EmuRate code                                *
\***************************************************************************/

static CycleCount EmuRate_LastUpdateCycle;
static clock_t EmuRate_LastUpdateTime;
uint32_t ARMul_EmuRate = 1000000; /* Start with safe value of 1MHz */

void EmuRate_Reset(ARMul_State *state)
{
  /* Reset the EmuRate code */
  EmuRate_LastUpdateCycle = ARMul_Time;
  EmuRate_LastUpdateTime = clock();
}

void EmuRate_Update(ARMul_State *state)
{
  uint64_t iocrate;
  clock_t nowtime, timediff;
  CycleCount nowcycle = ARMul_Time;
  CycleDiff cycles = nowcycle-EmuRate_LastUpdateCycle;
  /* Ignore if not much time has passed */
  if(cycles < 40000)
    return;
  nowtime = clock();
  timediff = nowtime-EmuRate_LastUpdateTime;
  if(timediff < 10)
    return;

  EmuRate_LastUpdateCycle = nowcycle;
  EmuRate_LastUpdateTime = nowtime;

  /* Update IOC timers before we calculate the new value */
  UpdateTimerRegisters(state);

  /* Calculate new rate */
  
#ifdef PROFILE_ENABLED
  /* Force 8MHz when profiling is on */
  ARMul_EmuRate = 8000000;
#else
  {
  uint32_t newrate = (uint32_t) ((((double)cycles)*CLOCKS_PER_SEC)/timediff);
  /* Clamp to a sensible minimum value, just in case something crazy happens */
  if(newrate < 1000000)
    newrate = 1000000;
  /* Smooth the value a bit, in case of sudden jumps, and to cope with systems with poor clock() granularity */
  ARMul_EmuRate = (ARMul_EmuRate*3+newrate)>>2;
  }
#endif

  /* Recalculate IOC rates */

  iocrate = (((uint64_t) 2000000)<<16)/ARMul_EmuRate;
  ioc.InvIOCRate = (((uint64_t) ARMul_EmuRate)<<16)/2000000;
  ioc.IOCRate = (uint32_t) iocrate;

  /* Update IOC timers again, to ensure the next interrupt occurs at the right time */
  UpdateTimerRegisters(state);

  //fprintf(stderr,"EmuRate %d IOC %.4f InvIOC %.4f\n",ARMul_EmuRate,((float)ioc.IOCRate)/65536,((float)ioc.InvIOCRate)/65536);  
}

